#include <cstring>
#include <ostream>
#include <string.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>

using namespace std;


class COWString{
public:
    COWString():_pstr(new char[5]()+4){

    }

    ~COWString(){
        if(getCount()==0){
            delete [] (_pstr-4);
        }
    }

    COWString(const char *p):_pstr(new char[strlen(p)+5]()+4){
        strcpy(_pstr,p);
        CountInit();
    }

    COWString(const COWString &rhs):_pstr(rhs._pstr){
        CountInc();
    }

    COWString& operator=(const COWString &rhs){
        if(this!=&rhs){
            CountDec();
            if(getCount()==0){
                delete[] (_pstr-4);
            }
            _pstr=rhs._pstr;
            CountInc();
        }
        return *this;
    }

    void CountInit(){
        *(int *)(_pstr-4)=1;
    }

    void CountInc(){
        ++*(int *)(_pstr-4);
    }

    void CountDec(){
        --*(int *)(_pstr-4);
    }
    
    size_t getCount(){
        return *(int *)(_pstr-4);
    }


private:
    class CharProXY{
        public:
            CharProXY(COWString &cow,size_t t)
                :_cow(cow)
                 ,index(t)
            {
                
            }

            char& operator=(const char &c){
                if(index>=strlen(_cow._pstr)){
                    static char nullchar='\0';
                    return nullchar;
                }

                if(_cow.getCount()>1){
                    _cow.CountDec();
                    char *temp=new char[strlen(_cow._pstr)+5]()+4;
                    strcpy(temp,_cow._pstr);
                    _cow._pstr=temp;
                    _cow.CountInit();
                }
                    _cow._pstr[index]=c;
                    return _cow._pstr[index];

            }
        friend ostream& operator<<(ostream& os,const CharProXY &rhs);
        private:
            COWString  &_cow;
            size_t index;
    };
    char* _pstr;
    
public:

    CharProXY operator[](size_t t){
        return CharProXY(*this,t);
    }

    friend ostream& operator<<(ostream& os,const COWString &rhs);
    friend ostream& operator<<(ostream& os,const CharProXY &rhs);

};

ostream & operator<<(ostream& os,const COWString &rhs){
    if(rhs._pstr)
        os<<rhs._pstr;
    return os;
}

ostream& operator<<(ostream& os,const COWString::CharProXY &rhs){
    if(rhs.index<strlen(rhs._cow._pstr)){
        os<<rhs._cow._pstr[rhs.index];
    }
    return os;
}

void test(){
    COWString s1("JAVA");
    cout<<"---\ts1:"<<s1<<"\t\ts1.count:"<<s1.getCount()<<"\t---"<<endl;
    cout<<endl;

    COWString s2=s1;
    cout<<"---\ts2:"<<s2<<"\t\ts2.count:"<<s2.getCount()<<"\t---"<<endl;
    cout<<"---\ts1:"<<s1<<"\t\ts1.count:"<<s1.getCount()<<"\t---"<<endl;
    cout<<endl;

    COWString s3(s2);
    cout<<"---\ts3:"<<s3<<"\t\ts3.count:"<<s3.getCount()<<"\t---"<<endl;
    cout<<"---\ts2:"<<s2<<"\t\ts2.count:"<<s2.getCount()<<"\t---"<<endl;
    cout<<"---\ts1:"<<s1<<"\t\ts1.count:"<<s1.getCount()<<"\t---"<<endl;
    
    cout<<endl;

    cout<<"---\ts1[6]:"<<s1[6]<<"\t\ts1.count:"<<s1.getCount()<<"\t---"<<endl;
    cout<<"---\ts2[1]:"<<s2[1]<<"\t\ts2.count:"<<s2.getCount()<<"\t---"<<endl;
    s2[2]='O';
    cout<<"---\ts2[2]='O',s2:"<<s2<<"\ts2.count:"<<s2.getCount()<<"\t---"<<endl;
    cout<<"---\ts1:"<<s1<<"\t\ts1.count:"<<s1.getCount()<<"\t---"<<endl;

}

int main()
{
 test();
 return 0;
}

